package br.com.puertorico.model.service.exceptions;

import java.util.Set;

import javax.persistence.OptimisticLockException;
import javax.validation.ConstraintViolation;

import org.apache.commons.lang.exception.ExceptionUtils;

import br.com.puertorico.common.exceptions.ApplicationException;
import br.com.puertorico.common.loggin.Logger;
import br.com.puertorico.common.utils.CommonUtils;

public class DatabaseException extends ApplicationException {

	private static final long serialVersionUID = 9040648697494188668L;

	private static final Logger _log = Logger
			.getLogger(DatabaseException.class);

	private Long idEntidade;

	private Class<?> classeEntidade;

	private String summary;

	private String detail;

	public DatabaseException(Throwable t, Long idEntidade,
			Class<?> classeEntidade) {
		if (t instanceof OptimisticLockException) {
			summary = "OptimisticLockException.summary";
			detail = "OptimisticLockException.detail";
			return;
		}

		setClasseEntidade(classeEntidade);
		setIdEntidade(idEntidade);
		setParams(idEntidade.toString(), classeEntidade.getSimpleName());
		_log.error("Erro durante acesso a base de dados: #0@#1", idEntidade,
				classeEntidade);
		Throwable root = ExceptionUtils.getRootCause(t);
		String message = null;
		if (root != null) {
			message = root.getMessage();
		}
		if (message == null) {
			message = t.getMessage();
		}
		_log.info(message);
		String key = searchUniqueConstraintViolation(message);

		if (key != null) {
			summary = "DatabaseException.summary.un-constraint-violation";
			detail = "DatabaseException." + key;
			return;
		}
		key = searchPrimaryKeyConstraintViolation(message);
		if (key != null) {
			summary = "DatabaseException.summary.pk-constraint-violation";
			detail = "DatabaseException." + key;
			return;
		}
		key = searchForeignKeyConstraintViolation(message);
		if (key != null) {
			summary = "DatabaseException.summary.fk-constraint-violation";
			detail = "DatabaseException." + key;
			return;
		}

	}

	public <ENTITY> DatabaseException(
			Set<ConstraintViolation<ENTITY>> constraintViolations) {
		this.summary = "Erro ao salvar registro";
		StringBuilder detail = new StringBuilder();
		for (ConstraintViolation<?> violation : constraintViolations) {
			detail.append(detail.toString().isEmpty() ? "" : "\n");
			detail.append(violation.getMessage());
			_log.error("#0: #1", violation.getPropertyPath(), detail);
		}
	}

	public Long getIdEntidade() {
		return idEntidade;
	}

	public void setIdEntidade(Long idEntidade) {
		this.idEntidade = idEntidade;
	}

	public Class<?> getClasseEntidade() {
		return classeEntidade;
	}

	public void setClasseEntidade(Class<?> classeEntidade) {
		this.classeEntidade = classeEntidade;
	}

	private String searchPrimaryKeyConstraintViolation(String message) {
		String key = CommonUtils.findPatternInString(message, "([a-z]+)_pkey");
		if (key != null && !key.isEmpty()) {
			return key + ".detail";
		}
		return null;
	}

	private String searchUniqueConstraintViolation(String message) {
		String key = CommonUtils
				.findPatternInString(message, "([_a-z.]+)_ukey");
		if (key != null && !key.isEmpty()) {
			return key + ".detail";
		}
		return null;
	}

	private String searchForeignKeyConstraintViolation(String message) {
		String key = CommonUtils.findPatternInString(message,
				"([a-z]+)_x_([a-z_]+)_fkey");
		if (key != null && !key.isEmpty()) {
			return key + ".detail";
		}
		return null;
	}

	public String getSummary() {
		return summary;
	}

	public String getDetail() {
		return detail;
	}
}
